﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Linq;
using System.Text;
using RemoteTestRunner.Contracts;
using System.Windows;
using RemoteTestRunner.BusinessLogic.Models;
using RemoteTestRunner.BusinessLogic;
using RemoteTestRunner.BusinessLogic.Views;
using System.ComponentModel;
using System.Windows.Input;
using System.Windows.Forms;

namespace RemoteTestRunner.BusinessLogic.ViewModels
{

    public class RunnerViewModel : IViewModelBase, INotifyPropertyChanged
    {
        //public DataView Projects { get; set; }
        //public DataView Tests { get; set; }

        #region Projects (INotifyPropertyChanged Property)
        private ObservableCollection<Project> _projects;

        public ObservableCollection<Project> Projects
        {
            get { return _projects; }
            set
            {
                if (_projects != value)
                {
                    _projects = value;
                    OnPropertyChanged("Projects");
                }
            }
        }
        #endregion

        #region SelectedProject (INotifyPropertyChanged Property)
        private Project _selectedProject;

        public Project SelectedProject
        {
            get { return _selectedProject; }
            set
            {
                if (_selectedProject != value)
                {
                    _selectedProject = value;
                    OnPropertyChanged("SelectedProject");
                }
            }
        }
        #endregion

        #region SelectedTest (INotifyPropertyChanged Property)
        private Test _selectedTest;

        public Test SelectedTest
        {
            get { return _selectedTest; }
            set
            {
                if (_selectedTest != value)
                {
                    _selectedTest = value;
                    OnPropertyChanged("SelectedTest");
                }
            }
        }
        #endregion

        public ObservableCollection<SelectedItem<TestResultSummary>> TestResultHistory { get; set; }
        //public ObservableCollection<object> TestClassesAndMethods { get; set; }

        #region TestClassesAndMethods (INotifyPropertyChanged Property)
        private IList<TestClass> _testClassesAndMethods;

        public IList<TestClass> TestClassesAndMethods
        {
            get { return _testClassesAndMethods; }
            set
            {
                if (_testClassesAndMethods != value)
                {
                    _testClassesAndMethods = value;
                    OnPropertyChanged("TestClassesAndMethods");
                }
            }
        }
        #endregion

        #region AllClassesAndMethodsSelected (INotifyPropertyChanged Property)
        private bool _allClassesAndMethodsSelected;

        public bool AllClassesAndMethodsSelected
        {
            get { return _allClassesAndMethodsSelected; }
            set
            {
                if (_allClassesAndMethodsSelected != value)
                {
                    _allClassesAndMethodsSelected = value;
                    OnPropertyChanged("AllClassesAndMethodsSelected");
                }
            }
        }
        #endregion

        //#region Project (INotifyPropertyChanged Property)
        //private DataRowView _property;

        //public DataRowView Project
        //{
        //    get { return _property; }
        //    set
        //    {
        //        if (_property != value)
        //        {
        //            _property = value;
        //            OnPropertyChanged("Project");
        //        }
        //    }
        //}
        //#endregion

        //#region Test (INotifyPropertyChanged Property)
        //private DataRowView _test;

        //public DataRowView Test
        //{
        //    get { return _test; }
        //    set
        //    {
        //        if (_test != value)
        //        {
        //            _test = value;
        //            OnPropertyChanged("Test");
        //        }
        //    }
        //}
        //#endregion

        //#region TestInfo (INotifyPropertyChanged Property)
        //private TestInfo _testInfo;

        //public TestInfo TestInfo
        //{
        //    get { return _testInfo; }
        //    set
        //    {
        //        if (_testInfo != value)
        //        {
        //            _testInfo = value;
        //            OnPropertyChanged("TestInfo");
        //        }
        //    }
        //}
        //#endregion

        #region TestResultSummary (INotifyPropertyChanged Property)
        private SelectedItem<TestResultSummary> _testResultSummary;
        public SelectedItem<TestResultSummary> TestResultSummary
        {
            get { return _testResultSummary; }
            set
            {
                if (_testResultSummary != value)
                {
                    _testResultSummary = value;
                    OnPropertyChanged("TestResultSummary");
                }
            }
        }
        #endregion

        #region TestResult (INotifyPropertyChanged Property)
        private TestResult _testResult;

        public TestResult TestResult
        {
            get { return _testResult; }
            set
            {
                if (_testResult != value)
                {
                    _testResult = value;
                    OnPropertyChanged("TestResult");
                }
            }
        }
        #endregion

        #region ShowDataDrivenOnly (INotifyPropertyChanged Property)
        public bool ShowDataDrivenOnly
        {
            get { return RtrSettings.FilterTests; }
            set
            {
                if (RtrSettings.FilterTests != value)
                {
                    RtrSettings.FilterTests = value;
                    OnPropertyChanged("ShowDataDrivenOnly");
                }
            }
        }
        #endregion

        private ITestRunnerWpfView _view;
        public RunnerViewModel(ITestRunnerWpfView view)
        {
            TestClassesAndMethods = new List<TestClass>();
            TestResultHistory = new ObservableCollection<SelectedItem<TestResultSummary>>();

            this.RunCommand = new RunCommandInternal(this);
            DeleteSelectedHistoryItemsCommand = new RelayCommand(DeleteSelectedHistoryItemsCommandExecute);

            this.ProjectAdminCommand = new ProjectAdminCommandInternal(this);
            TestResult = new TestResult();

            Projects = new ObservableCollection<Project>();

            List<Project> projects = RemoteTestRunner.BusinessLogic.Models.RtrServiceResolver.Service.GetProjects();
            Projects.Clear();
            projects.ForEach(p => Projects.Add(p));

            //_testData = RemoteTestRunner.BusinessLogic.Models.RtrServiceResolver.Service.GetTestData();
            _view = view;
            _view.SetRootDataContext(this);

            PropertyChanged += new PropertyChangedEventHandler(RunnerViewModel_PropertyChanged);
        }

        void RunnerViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            try
            {
                switch (e.PropertyName)
                {
                    case "SelectedProject":
                        break;

                    case "SelectedTest":
                        LoadTestClassesAndMethods();
                        LoadTestRunHistory();
                        break;

                    case "TestResultSummary":
                        DisplayTestResults();
                        break;

                    case "ShowDataDrivenOnly":
                        LoadTestClassesAndMethods();
                        break;

                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        //private DataSet _testData;
        //private void LoadProjectsInView(object sender, EventArgs e)
        //{
        //    Projects = new DataView(_testData.Tables["Project"]);

        //    //_view.SetProjects(projects);

        //    if (Projects.Count == 1)
        //    {
        //        Project = Projects[0];
        //        //_view.Project = projects[0];
        //        //ProjectSelected();
        //    }
        //    else
        //    {
        //        Project = null;
        //        //ProjectSelected();
        //    }
        //}

        //private void ProjectSelected()
        //{
        //    if (Project != null && Project["ID"] != null)
        //    {
        //        Tests =
        //            new DataView(
        //                _testData.Tables["Test"],
        //                "ProjectId = " + Project["ID"],
        //                string.Empty,
        //                DataViewRowState.CurrentRows);
        //    }
        //    else
        //    {
        //        Tests = new DataView();
        //    }

        //    if (Tests.Count == 1)
        //    {
        //        Test = Tests[0];
        //    }
        //    else
        //    {
        //        Test = null;
        //    }
        //    //TestSelected();
        //}

        //private void DisplayTestInformation()
        //{
        //    //if (Test == null)
        //    //{
        //    //    TestInfo = new TestInfo();
        //    //}
        //    //else
        //    //{
        //    //    TestInfo = RtrServiceResolver.Service.GetTestRow((int)Test["ID"]);
        //    //}

        //    LoadTestClassesAndMethods();

        //    LoadTestRunHistory();
        //    //DisplayTestResults();

        //    //LoadDataSourceMenu();
        //}

        private void DisplayTestResults()
        {
            if (TestResultSummary == null)
            {
                TestResult = new TestResult();
            }
            else
            {
                TestResult = RtrServiceResolver.Service.GetTestResult(TestResultSummary.Item.Path);
                _view.SetDetailedResults(TestResult.MultiAssertReport);
            }
        }

        private void LoadTestClassesAndMethods()
        {
            TestClassesAndMethods.Clear();
            if (SelectedTest == null)
            {
                // do nothing, leave the list empty
            }
            else
            {
                IList<TestClass> testMethods = RtrServiceResolver.Service.GetTestMethods(SelectedTest);

                // Filter out non-data-driven tests if the option is turned on
                if (RtrSettings.FilterTests)
                {
                    for (var classIndex = testMethods.Count - 1; classIndex >= 0; classIndex--)
                    {
                        for (var methodIndex = testMethods[classIndex].TestMethods.Count - 1;
                             methodIndex >= 0;
                             methodIndex--)
                        {
                            if (!testMethods[classIndex].TestMethods[methodIndex].DataDriven)
                            {
                                testMethods[classIndex].TestMethods.RemoveAt(methodIndex);
                            }
                        }

                        // Remove any class nodes that no longer have any method nodes
                        if (testMethods[classIndex].TestMethods.Count == 0)
                        {
                            testMethods.RemoveAt(classIndex);
                        }
                    }
                }

                TestClassesAndMethods = testMethods;
            }
        }

        /// <summary>
        /// Fills the previous test runs.
        /// </summary>
        private void LoadTestRunHistory()
        {

            TestResultHistory.Clear();
            if (SelectedTest == null)
            {
                // nothing to do, leave the list empty.
            }
            else
            {
                IList<TestResultSummary> testResults = RtrServiceResolver.Service.GetTestResultHistory(SelectedProject, SelectedTest);

                // Sort the results by start time descending.
                var orderedResults = testResults.OrderBy(result => result.TestStartTime).Reverse();

                foreach (TestResultSummary result in orderedResults)
                {
                    TestResultHistory.Add(new SelectedItem<TestResultSummary>(result));
                }

                if (TestResultHistory.Count > 0)
                {
                    TestResultSummary = TestResultHistory[0];
                }
            }
        }

        /// <summary>
        /// Gets the checked nodes.
        /// </summary>
        /// <returns>An IList of all the checked nodes.</returns>
        private List<string> GetSelectedClassesAndMethods()
        {
            var returnList = new List<string>();
            if (AllClassesAndMethodsSelected)
            {
                // root is checked.  Return an empty list so all tests will run.
            }
            else
            {
                foreach (TestClass testClass in TestClassesAndMethods)
                {
                    if (testClass.IsSelected)
                    {
                        // Add whole class
                        returnList.Add(testClass.Name);
                    }
                    else
                    {
                        foreach (TestMethod testMethod in testClass.TestMethods)
                        {
                            if (testMethod.IsSelected)
                            {
                                returnList.Add(testClass.Name + "." + testMethod.Name);
                            }
                        }
                    }
                }
            }

            return returnList;
        }

        #region IViewModelBase Members

        public void Start()
        {
            //LoadProjectsInView(this, EventArgs.Empty);
            _view.Show();
        }

        public IWpfViewBase View
        {
            get { return _view; }
        }

        #endregion

        #region Commands

        public ICommand DeleteSelectedHistoryItemsCommand { get; set; }

        private void DeleteSelectedHistoryItemsCommandExecute(object parameters)
        {
            var selectedItems = from item in TestResultHistory
                                where item.IsSelected
                                select item.Item;

            RtrServiceResolver.Service.DeleteTestResultHistoryItems(selectedItems.ToList());

            LoadTestRunHistory();
        }

        #region RunCommand

        public ICommand RunCommand { get; set; }

        internal class RunCommandInternal : ICommand
        {

            private RunnerViewModel _viewModel;
            public RunCommandInternal(RunnerViewModel viewModel)
            {
                _viewModel = viewModel;
                _viewModel.PropertyChanged += new PropertyChangedEventHandler(_viewModel_PropertyChanged);
            }


            void _viewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                switch (e.PropertyName)
                {
                    case "SelectedTest":
                        OnCanExecuteChanged();
                        break;
                    default:
                        break;
                }
            }

            private void OnCanExecuteChanged()
            {
                if (CanExecuteChanged != null)
                    CanExecuteChanged(this, EventArgs.Empty);
            }

            #region ICommand Members

            public bool CanExecute(object parameter)
            {
                return _viewModel.SelectedTest != null;
            }

            public event EventHandler CanExecuteChanged;

            public void Execute(object parameter)
            {
                try
                {
                    var userState = new List<object>();

                    var longRunning = new object(); // IoC.Container.Resolve<LongRunningPresenter>();
                    userState.Add(longRunning);

                    RtrLib.RunTestDelegate runTestDelegate = RtrServiceResolver.Service.RunTest;
                    userState.Add(runTestDelegate);

                    runTestDelegate.BeginInvoke(
                        _viewModel.SelectedProject,
                        _viewModel.SelectedTest,
                        _viewModel.GetSelectedClassesAndMethods(),
                        RtrSettings.TestTimeoutSeconds,
                        ThreadSafeRunTestCompleted,
                        userState);

                    //longRunning.Description = "Running test(s)...";
                    //longRunning.Start();

                }
                catch (Exception ex)
                {
                    throw;
                    //MessageBox.Show(
                    //    ex.ToString(), "Test Run Message", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }

            }

            #endregion

            /// <summary>
            /// Completion method for async RunTest.
            /// </summary>
            /// <param name="ar">The async result.</param>
            private void ThreadSafeRunTestCompleted(IAsyncResult ar)
            {
                AsyncCallback callback = RunTestCompleted;
                _viewModel.View.Dispatcher.BeginInvoke(callback, new object[] { ar });
                return;
            }

            /// <summary>
            /// Completion method for async RunTest.
            /// </summary>
            /// <param name="ar">The async result.</param>
            private void RunTestCompleted(IAsyncResult ar)
            {
                var userState = (List<object>)ar.AsyncState;
                //var longRunning = (LongRunningPresenter)userState[0];

                //longRunning.Stop();

                var runTestDelegate = userState[1] as RtrLib.RunTestDelegate;

                TestResultSummary summary;

                try
                {
                    summary = runTestDelegate.EndInvoke(ar);
                }
                catch (Exception ex)
                {
                    summary = null;
                    throw;
                    //MessageBox.Show(
                    //    ex.ToString(), "Test Run Message", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }

                if (summary != null)
                {
                    // Save the run that just completed for selecting.
                    string savedId = summary.TestRunId;

                    // re-populate the test runs now that we have a new one.
                    // TODO come up with a better way to populate test runs.  Maybe use a file watcher (this might not work for the remote version).
                    _viewModel.LoadTestRunHistory();
                    _viewModel.TestResultSummary =
                        _viewModel.TestResultHistory.FirstOrDefault(result => result.Item.TestRunId == savedId);
                }
            }
        }

        #endregion

        #region ProjectAdminCommand

        public ICommand ProjectAdminCommand { get; set; }

        internal class ProjectAdminCommandInternal : ICommand
        {

            public ProjectAdminCommandInternal(RunnerViewModel viewModel)
            {
            }

            #region ICommand Members

            public bool CanExecute(object parameter)
            {
                return true;
            }

            public event EventHandler CanExecuteChanged;

            public void Execute(object parameter)
            {
                var projectAdmin = IoC.Container.Resolve<ProjectAdminViewModel>();
                projectAdmin.Start();
            }

            #endregion
        }

        #endregion

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}
